/*
#    dir.c
#    SCHIPS
#    schips@dingtalk.com
#    https://gitee.com/schips/
#    Thu, Jun 13, 2019  2:34:19 PM
*/

#include <stdio.h>
#include <string.h>
#include <sys/stat.h> 
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include "dir_api.h"

// linux下C语言创建多级目录 
int create_dirs(const char *sPathName, unsigned int mode)
{  
    char DirName[256];  
    strcpy(DirName, sPathName);  
    int i,len = strlen(DirName);  

    if(DirName[len-1] != '/')  { strcat(DirName, "/");  }

    len = strlen(DirName);  
    for(i = 1; i < len; i++)  
    {  
        if(DirName[i] == '/')  
        {  
            DirName[i] = '\0';  
            if( access(DirName, F_OK)!= 0 )  
            {  
                if(mkdir(DirName, mode) == -1)  {   return -1; }  
            }
            DirName[i] = '/';  
        } 
    }
    return  0;  
}

/**
 * 递归删除目录(删除该目录以及该目录包含的文件和目录)
 * @dir:要删除的目录绝对路径
 * https://blog.csdn.net/zhaoxd200808501/article/details/75007762
 */
int remove_dir(const char *dir)
{
    char cur_dir[] = ".";
    char up_dir[] = "..";
    char dir_name[512];
    DIR *dirp;
    struct dirent *dp;
    struct stat dir_stat;

    // 参数传递进来的目录不存在，直接返回
    if ( 0 != access(dir, F_OK) ) { return 0; }

    // 获取目录属性失败，返回错误
    if ( 0 > stat(dir, &dir_stat) ) {
        // perror("get directory stat error");
        return -1;
    }

    if ( S_ISREG(dir_stat.st_mode) ) {  // 普通文件直接删除
        remove(dir);
    } else if ( S_ISDIR(dir_stat.st_mode) ) {   // 目录文件，递归删除目录中内容
        dirp = opendir(dir);
        while ( (dp=readdir(dirp)) != NULL ) {
            // 忽略 . 和 ..
            if ( (0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name)) ) {
                continue;
            }

            sprintf(dir_name, "%s/%s", dir, dp->d_name);
            remove_dir(dir_name);   // 递归调用
        }
        closedir(dirp);

        rmdir(dir);     // 删除空目录
    } else {
        // perror("unknow file type!");    
    }

    return 0;
}

// 取上级目录
int get_previous_pathname(char *fileNameWithPath, char *outputPathName)
{
    char buf[256];
    int  len;;

    len = strlen(fileNameWithPath);
    strcpy(buf,  fileNameWithPath);

    while(len-- != 0)
    {
        if(buf[len] == '/')
        {
            buf[++len] = '\0';
            break;
        }
    }

    if(len !=0)
    {
        strcpy(outputPathName,buf);
        return 0;
    }else
    {
        return -1;
    }
}


// 文件拷贝（仅考虑内容，对创建时间等不做处理）
int file_copy(char *path_r,char *path_w)
{
    int fd_r;
    int fd_w;
    ssize_t rd_size, wr_size;
    char buffer[4*1024];
    char *poffs;
    if(!path_r || !path_w){ return -1; }


    //DEBUG_GREEN("cp %s to %s",path_r, path_w);
    //return 0;
    fd_r = open(path_r, O_RDONLY);
    fd_w = open(path_w, O_WRONLY|O_CREAT|O_TRUNC, 
                S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);

    if(fd_r == -1 )  { return -1; }
    if(fd_w == -1)   { close(fd_r);  return -1;   }

    while(1)
    {
        rd_size = read(fd_r, buffer, sizeof(buffer));
        if(rd_size == -1)
        {
            perror("read file error\n");
            goto err;
        }

        if(rd_size == 0)
            break;

        poffs = buffer;

        while(1)
        {
            wr_size = write(fd_w, poffs, rd_size); // 实际写入长度wr_size 只会小于或等于预设写入长度rd_size
            if(wr_size == -1)
            {
                goto err;
            }

            if(wr_size == rd_size) // 当 实际写入长度 小于 预设写入长度，必须进行处理，以防丢包
                break;

            rd_size  -=  wr_size;
            poffs +=  wr_size;
        }
    }

    close(fd_w);
    close(fd_r);

    return 0;
err:
    return -1;
}

// 路径与路径的对拷
int copy_path(char *source_path,char *destination_path)
{
    // printf("%s \t\t to \t\t %s\n", source_path, destination_path);
    if(!source_path)  {  return -1;  }
    if(!opendir(destination_path))
    {
        if(create_dirs(destination_path, 0755)==-1) {  return -1;  }
    }

    char path[512] = {0};
    char file_source_path[512] = {0};
    char file_destination_path[512] = {0};
    DIR* dp;
    struct dirent* filename;

    strncpy(path, source_path, strlen(source_path));
    dp = opendir(path);
    if(dp == NULL) {  return -1;  }
    while((filename = readdir(dp)) != NULL)
    {
        //遍历DIR指针指向的文件夹，也就是文件数组。
        memset(path, 0, sizeof(path));
        sprintf(path, "%s", source_path);
        // 确保 source_path,destination_path以路径分隔符结尾

        if(!endwith(source_path,'/'))
        {
            sprintf(file_source_path, "%s/",source_path);
        }
        else
        {
            sprintf(file_source_path, "%s",source_path);
        }

        if(!endwith(destination_path,'/'))
        {
            sprintf(file_destination_path,"%s/",destination_path);
        }
        else
        {
            sprintf(file_destination_path,"%s",destination_path);
        }
        //取文件名与当前文件夹拼接成一个完整的路径
        sprintf(file_source_path,"%s%s",file_source_path,filename->d_name);
        sprintf(file_destination_path,"%s%s",file_destination_path, filename->d_name);

        if(is_dir(file_source_path))
        {
            //如果是目录
            if(!endwith(file_source_path,'.')) //同时并不以.结尾，
            {
                copy_path(file_source_path,file_destination_path);//进行递归调用，相当于进入这个文件夹进行复制～
            }		
        }
        else{
            file_copy(file_source_path, file_destination_path);
        }
    }
    return 0;
}



// 将一个路径拆分成 文件/目录 部分 + 所在路径 2个部分；
int spit_pathname_to_file_and_dir(const char * pathname, char * filename_out, char * pathname_out)
{
    int i, j;
    int found = 0;// right_slash = 0;
    int len;
    char * tmp;     //用于找到 文件名
    char * tmp_pathname; // 用于储存路径，以便于在这个基础上进行修改

    // printf("参数名： %s\n", pathname);

    if( !pathname && ! pathname_out && !filename_out)  {  return ERR;  }

    len = strlen(pathname);
    if(len == 0)   { return ERR;   }

    tmp_pathname = malloc(sizeof(char)* (len + 1));
    if(!tmp_pathname){ return ERR; }

    //strncpy(tmp_pathname, pathname, len);
    sprintf(tmp_pathname, "%s", pathname);
    tmp = tmp_pathname; 

    for (i = len - 1, found = 0, j = 0; i >= 0; i--, j++) 
    {
        if(tmp_pathname[i] == '\\' || tmp_pathname[i] == '/')
        {
            if(i == len - 1)
            {
                tmp_pathname[i] = '\0';  continue;  
            }
            else
            {
                tmp = tmp_pathname + i + 1; found = 1;
                tmp_pathname[i] = '\0';
                break;
            }
        }
    }

    if(found)
    {
        if(pathname_out)
        {
            sprintf(pathname_out, "%s", tmp_pathname);
        }
        if(filename_out)
        {
            sprintf(filename_out, "%s", tmp);
            //strncpy(filename_out, tmp,  j+1);
            //filename_out[j+1] = '\0';
        }

    }else if(i < 0 )
    {
        if(pathname_out)
        {
            pathname_out[0] = '.';
            pathname_out[1] = '\0';
        }
        if(filename_out)
        {
            strncpy(filename_out, tmp_pathname, len+1);
            filename_out[len+1] = '\0';
        }
    }

    free(tmp_pathname);

    return OK;
}


// 一个cp 函数的实现
int copy_path_to(char * path_src, char *path_des)
{
    // 判断不同的拷贝情况，并做出对应的处理
    struct stat st;
    char destination_path[512] = {0};
    char source_path[512] = {0};
    char filename_without_path[256];
    char tmp_filename[512] = {0};
    char tmp_pathname[512] = {0};

    if(!path_src || !path_des){ return -1; }

    if ( 0 != access(path_src, F_OK) ) { return 0; }
    sprintf(source_path, "%s", path_src);
    sprintf(destination_path, "%s", path_des);

    stat(source_path, &st);
    if (S_ISDIR(st.st_mode))
    {
        stat(destination_path, &st);
        if (S_ISDIR(st.st_mode))    
        {

            spit_pathname_to_file_and_dir(source_path, tmp_filename, tmp_pathname);
            // DEBUG_RED("%s", tmp_pathname);
            // DEBUG_RED("%s", tmp_filename);
            if(!endwith(destination_path,'/'))
            {
                sprintf(destination_path,"%s/%s",destination_path,tmp_filename);
            }else
            {
                sprintf(destination_path,"%s%s",destination_path,tmp_filename);
            }
            // printf("copy path %s to path %s\n", source_path, destination_path);
            if(strstr(source_path, destination_path))
            {  /*DEBUG_YELLOW("?")*/; return 0;  }
            copy_path(source_path, destination_path);
        }else                       
        {   return -1;/* 不允许将 目录 拷贝到 文件 */  }
    }else
    {
        stat(destination_path, &st);
        if (S_ISDIR(st.st_mode))
        {
            spit_pathname_to_file_and_dir(source_path, filename_without_path, NULL);
            if(!endwith(destination_path,'/'))
            {
                sprintf(destination_path,"%s/%s",destination_path,filename_without_path);
            }else
            {
                sprintf(destination_path,"%s%s",destination_path,filename_without_path);
            }
        }

        if(strstr(source_path, destination_path)) // printf("same, do nothing\n");
        {  return 0;  }
        // printf("copy file %s to file %s\n", source_path, destination_path);
        file_copy(source_path, destination_path);
    }
    return 0;
}


int main(int argc,char **argv) 
{ 
    int ret; 
    char path[512]  = {0};
    char path2[512] = {0};
#if 1
    // 拆分目录 到文件名 以及目录的测试
    ret = spit_pathname_to_file_and_dir(NULL, NULL,  NULL);
    printf("文件名： %s\n", path);
    printf("路径名： %s\n\n", path2);
    memset(path , 0, sizeof(path));
    memset(path2, 0, sizeof(path2));

    ret = spit_pathname_to_file_and_dir("/homeschips.local", path,  NULL);
    printf("文件名： %s\n", path);
    printf("路径名： %s\n\n", path2);
    memset(path , 0, sizeof(path));
    memset(path2, 0, sizeof(path2));
#endif

    copy_path_to("test1", "test2");
#if 0
    // 将目录A拷贝到目录B
    //copy_path("1","2");
#endif
#if 0
    //创建多级目录 
    //create_dirs("12/45/56");
#endif
    return ret; 
} 


